package com.sherlocky.util;


import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.FileUtil;
import org.apache.commons.vfs.VFS;
import org.springframework.web.multipart.MultipartFile;

import com.sherlocky.config.dao.ConfigDAO;
import com.sherlocky.exception.FileOperationException;
import com.sherlocky.exception.ShowMessageException;

import conf.AppContextHolder;


/**
 * 实现基本的VFS功能
 * @author zhujj
 *
 */
public class VFSUtil {
	
	private static final Log log = LogFactory.getLog(VFSUtil.class);
	//VFS路径，以/结尾
	private static String VFS_ROOT_PATH = "";
	
	private static String BREAK_STR="$$$$";
	
	static{
		try{		
			readVFSRootPath();
			new File(VFS_ROOT_PATH).mkdirs();
		}catch(Exception e){
			log.error("创建VFS失败["+VFS_ROOT_PATH+"]", e);
			throw new FileOperationException("创建VFS失败["+VFS_ROOT_PATH+"]");
		}
	}
	
	private static void readVFSRootPath() {
		String key = null;
		if (isWindows()) {
			key = "VFS_ROOT_PATH_WINDOWS";
		} else {
			key = "VFS_ROOT_PATH_LINUX";
		}
		try {
			//JdbcTemplate jdbcTemplate = new JdbcTemplate((DataSource)AppContextHolder.getContext().getBean("dataSource"));
			//VFS_ROOT_PATH = jdbcTemplate.queryForObject("select value from lx_config where var = ?", new Object[]{key}, String.class);
			VFS_ROOT_PATH = AppContextHolder.getContext().getBean(ConfigDAO.class).getValue(key);
		} catch ( Exception e ) {
			log.error("读取vfs配置出错，使用默认配置", e);
		}
		if (StringUtils.isBlank(VFS_ROOT_PATH)) {
			if (isWindows()) {
				VFS_ROOT_PATH =  "D:/vfsroot"; 
			} else {
				VFS_ROOT_PATH = "/opt/vfsroot"; 
			}
			VFS_ROOT_PATH += "/lexue";
		}
	}
	
	
	private static boolean isWindows() {
		return System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") != -1;
	}
	
	/**
	 * 获取当前的VfsRootPath  
	 * @return
	 */
	public static String getVFSRootPath(){
		return normalize(VFS_ROOT_PATH);
	}
	/**
	 * @param filePath 基于VFS根路径的文件路径
	 * @return
	 */
	public static String getVFSPath(String filePath) {
		return normalize(VFS_ROOT_PATH + "/" + filePath);
	}
	
	/**
	 * 根据文件路径查找某个文件
	 * @param filePath
	 * @return
	 */
	public static File getFile(String filePath) {
		if(filePath==null){
			return null;
		}
		return new File(getVFSPath(filePath));
	}
	
	/**
	 * 根据文件路径查找某个文件，如果文件不存在的话则创建这个文件
	 * @param filePath
	 * @return
	 */
	public static File getFileIsNotExistCreate(String filePath) {
		File file = new File(getVFSPath(filePath));
		if(!file.exists()) {
			file = recursiveMakeDirectory(filePath, VFS_ROOT_PATH);
		}
		return file;
	}
	
	public static boolean isFileExist(String filePath) {
		File file = new File(getVFSPath(filePath));
		return file.exists();
	}
	
	/**
	 * 列出某个文件夹下的子文件，不包括目录
	 * @param filePath
	 * @return
	 */
	public static List<File> listChildFiles(String filePath) {
		File parentFile = new File(getVFSPath(filePath));
		List<File> childFiles = new ArrayList<File>();
		if(parentFile.exists() && parentFile.canRead()) {
			File[] files = parentFile.listFiles();
			for (File file : files) {
				if(file.isFile()) {
					childFiles.add(file);
				}
			}
		}
		return childFiles;
	}
	
	public static List<File> listChildFilesAndDirs(String filePath) {
		File parentFile = new File(getVFSPath(filePath));
		List<File> childFiles = new ArrayList<File>();
		if(parentFile.exists() && parentFile.canRead()) {
			File[] files = parentFile.listFiles();
			childFiles = Arrays.asList(files);
		}
		return childFiles;
	}
	
	/**
	 * 删除文件，若上一级目录为空，删除目录
	 * @param path
	 */
	public static void removeFileAndDir(String path){
		String filedirpath = path.substring(0, path.lastIndexOf("/"));
		File dirFile = new File(getVFSPath(filedirpath));
		if(dirFile.exists() && dirFile.canRead()){
			File[] list = dirFile.listFiles();
			if(list.length <= 1){
				try {
					FileUtils.deleteDirectory(dirFile);
				} catch (IOException ex) {
					// TODO Auto-generated catch block
					throw new FileOperationException("删除文件目录失败,请检查目录下文件是否正在被使用", ex);
				}
			}else{
				removeFile(path);
			}
		}
	}
	
	/**
	 * 删除VFS下的某个文件
	 * @param filePath
	 */
	public static void removeFile(String filePath) {
		File file = new File(getVFSPath(filePath));
		if(file.exists()) {
			try{
				FileUtils.forceDelete(file);
			}catch(IOException ex) {
				throw new FileOperationException("删除文件失败,请检查文件是否正在被使用", ex);
			}
		}
	}
	
	public static void saveFile(MultipartFile thefile, String path) {
		try {
			thefile.transferTo(VFSUtil.getFileIsNotExistCreate(path));
		} catch (Exception e) {
			throw new FileOperationException("保存文件出错",e);
		}
	}
	
	public static void saveFile(byte[] fileContent, String path) {
		try {
			String filedirpath = path.substring(0, path.lastIndexOf("/"));
			String filename = path.substring(path.lastIndexOf("/")+1);
			File destFile = VFSUtil.getFileIsNotExistCreate(filedirpath);
			File file = new File(destFile.getAbsolutePath()+"/"+filename);
			FileUtils.writeByteArrayToFile(file, fileContent);
		} catch (Exception e) {
			throw new FileOperationException("保存文件出错",e);
		}
	}
	
	public static void saveFile(InputStream is, String path) {
		path = normalize(path);
		OutputStream out = null;
		try {
			String filedirpath = path.substring(0, path.lastIndexOf("/"));
			String filename = path.substring(path.lastIndexOf("/")+1);
			File destFile = VFSUtil.getFileIsNotExistCreate(filedirpath);
			
			File file = new File(destFile.getAbsolutePath()+"/"+filename);
			if(file.exists()) {
				file.delete();
			}
			file.createNewFile();
			
			out = new FileOutputStream(file);
			byte[] buf = new byte[1024];
			int len = 0;
			while ((len = is.read(buf)) != -1) {
				out.write(buf, 0, len);
			}
		} catch (Exception e) {
			throw new FileOperationException("保存文件出错",e);
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				throw new FileOperationException("保存文件出错",e);
			}
			try {
				if(out != null)out.close();
			} catch (IOException e) {
				throw new FileOperationException("保存文件出错",e);
			}
		}
	}
	
	/**
	 * 拷贝文件到某个目录（如果不存在则新建）下
	 * @param fromFilePath
	 * @param destDir
	 * @param isIncludeSelf 如果源文件是目录的话是否拷贝自己
	 */
	public static void copyVFSFile(String fromFilePath, String destDir, boolean isIncludeSelf) {
		fromFilePath = getVFSPath(fromFilePath);
		destDir = getVFSPath(destDir);
		File fromFile = new File(fromFilePath);
		if(!fromFile.exists()) {
			return;
		}
		File destDirectory = new File(destDir);
		if(destDirectory.exists() && !destDirectory.isDirectory()) {
			throw new FileOperationException("拷贝文件失败,目标文件不是目录");
		}
		if(fromFile.isFile()) {
			try{
				FileUtils.copyFileToDirectory(fromFile, destDirectory);
			}catch(IOException ex){
				throw new FileOperationException(
						"拷贝文件【"+fromFilePath+"】到目录【"+destDir+"】下失败，失败原因：" + ex.getMessage(), ex);
			}
		}
		if(fromFile.isDirectory()) {
			if(fromFile.list().length == 0) { //空文件夹
				return;
			}
			try {
				if(isIncludeSelf) {
					FileUtils.copyDirectoryToDirectory(fromFile, destDirectory);
				}else{
					FileUtils.copyDirectory(fromFile, destDirectory);
				}
			} catch (IOException ex) {
				throw new FileOperationException(
						"拷贝目录【"+fromFilePath+"】到目录【"+destDir+"】下失败，失败原因：" + ex.getMessage(), ex);
			}
		}
	}

	
	/**
     * 允许创建多级目录，目录名之间用/隔开，暂不支持创建失败回滚的功能
     * @param  fileName 要创建的目录名
     * @return 已创建的最底层的目录
     */
	private static File recursiveMakeDirectory(String fileName, String root) {
		root = normalize(root);
		StringTokenizer directories = new StringTokenizer(normalize(fileName), "/");
		File parent = new File(root);
		while (directories.hasMoreTokens()) {
			String name = directories.nextToken();
			parent = new File(parent, name);

			if (log.isInfoEnabled()) {
				log.info("will create the directory [" 
				                                     + parent.getAbsolutePath() + "]");
			}
			if (!parent.exists()) {
				if (log.isInfoEnabled()) {
					log.info("creating the directory [" 
							+ parent.getAbsolutePath() + "]");
				}

				if (!parent.mkdir()) {
					String msg = "fail to created the directory [" 
						+ parent.getAbsolutePath() + "]";
					if (log.isInfoEnabled()) {
						log.info(msg);
					}
					throw new FileOperationException(msg);
				}
			}
		}
		return parent;
	}
	
	/**
	 * 对不规则的文件路径进行解析,比如\\替换成/
	 * @param original
	 * @return
	 */
	public static String normalize(String original) {
        if (log.isDebugEnabled()) {
        	log.debug("normalize前[" + original + "]");
        }
        
        original = original.replace('\\', '/');
        original = eliminateRedundantSlassh(original);
        
        /**
         * in Linux(Unix like) system, must add prefix "/", unckecked,
         * and in WindowNT, if there is no ":", shoulb add one.
         */
        if (original.indexOf(':') == -1) {
            if (!original.startsWith("/")) {
                original = "/" + original;
            }
        } else {
            if (original.startsWith("/")) {
                original = original.substring(1);
            } 
        }

        if (original.endsWith("/")) {
            original = original.substring(0, original.length() - 1);
        }
        
        if (log.isDebugEnabled()) {
        	log.debug("normalize后[" + original + "]");
        }
        
        return original;
    }
	
	/**
     * 从路径名称中排除多余的 "/", 如果路径中有"\", 结果我没有测试过; 通常调用这个方法之前
     * 一定要把 "\" 转成 "/"
     * @return  返回排除了多余的"/"的路径
     */
    private static String eliminateRedundantSlassh(String path) {
        if (log.isDebugEnabled()) {
        	log.debug("要排除多余的'/'之前[" + path + "]");
        }
        
        boolean isSlash = false;
        StringBuffer result = new StringBuffer(path.length());
        
        for (int i = 0; i < path.length(); i++) {
            char c = path.charAt(i);
            
            if (c != '/' || !isSlash) {
                result.append(c);
            }
            
            isSlash = (c == '/');
        }

        if (log.isDebugEnabled()) {
        	log.debug("要排除多余的'/'之前[" + path + "]");
        }
        
        return result.toString();
    }
	
	public static void downloadFile(HttpServletResponse response, 
			HttpServletRequest request, String filePath, String downloadFileName) throws Exception{
		File file = new File(getVFSPath(filePath));
		if(!file.exists()) {
			throw new ShowMessageException("文件不存在！");
		}
		
		InputStream reader = null;
		OutputStream out = null;
		try {
			reader = getInputStream(file, true);
			byte[] buf = new byte[1024];
			int len = 0;
			response.setContentType("application/octet-stream");// 均不提供直接打开
			response.setHeader("Content-Disposition",
					"attachment;filename=" + HttpUtils.convert(request, downloadFileName));
			out = response.getOutputStream();
			while ((len = reader.read(buf)) != -1) {
				out.write(buf, 0, len);
			}
			out.flush();
		} catch (IOException e) {
			log.error("读取文件["+filePath+"]报错", e);
		} finally{
			if(reader != null){
				try{
					reader.close();
				}catch(Exception ex){
					log.error("关闭文件流出错", ex);
				}
			}
			if(out != null) {
				try{
					out.close();
				}catch(Exception ex){
					log.error("关闭输出流出错", ex);
				}
			}
		}
	}
	
	 public static InputStream getInputStream(File file,boolean fileStream){
	    	/**
	    	 * @author guiyf
	    	 * Apr,04,05 18:33 
	    	 * 当前情况下不需要区分，不存在还是不可访问，而且试读一次文件流没有必要(可能占用大量内存)。
	    	 */
	    	if(fileStream == true){//使用文件流
	    		FileInputStream fin = null;
	    		try {
	    			fin = new FileInputStream(file);

	    		} catch (FileNotFoundException e) {
	    			if (log.isDebugEnabled()) {
	    				log.debug(e);
	    			}
	    			String msg = "找不到指定的文件[" + file.getName() + "]。";
	    			if (log.isDebugEnabled()) {
	    				log.debug(msg);
	    			}
	    			throw new FileOperationException(msg,e);
	    		}
	    		return fin;
	    	}else{	//使用内存流
	    		InputStream in = null;
	    		if (file != null && file.canRead() && file.isFile()) {
	    			try {
	    				ByteArrayOutputStream buffer = new ByteArrayOutputStream();
	    				FileInputStream stream = new FileInputStream(file);
	    				BufferedInputStream bin = new BufferedInputStream(stream);
	    				int len = 0;
	    				byte[] b = new byte[1024];
	    				while ((len = bin.read(b)) != -1) {
	    					buffer.write(b, 0, len);
	    				}

	    				stream.close();
	    				in = new ByteArrayInputStream(buffer.toByteArray());
	    			} catch (Exception e) {
	    				String msg = "不能读取文件[" + file.getName() + "]";
	    				if (log.isErrorEnabled()) {
	    					log.error(msg, e);
	    				}
	    				throw new FileOperationException(msg, e);
	    			}
	    		} else {
	    			String msg = "不是文件或文件不可读[" + file.getName() + "]";
	    			if (log.isDebugEnabled()) {
	    				log.debug(msg);
	    			}
	    			throw new FileOperationException("不是文件或文件不可读");
	    		}
	    		return in;
	    	}
	    }
	
	/**
	 * 
	 * @param response
	 * @param url http://commons.apache.org/vfs/apidocs/org/apache/commons/vfs/FileSystemManager.html
	 * @throws Exception
	 */
	public static void showImage(HttpServletResponse response, String url)  throws Exception{
		FileSystemManager mgr = VFS.getManager();
		FileObject file = mgr.resolveFile(url);
		if(!file.exists()) {
			log.error("找不到文件[" + url + "]");
			return;
		}
		response.setContentType("image/jpg");    
		try{
			OutputStream out = response.getOutputStream();
			FileUtil.writeContent(file, out);
		}catch(Exception ex){
			log.error("显示照片时发生错误:" + ex.getMessage(),ex);
		}
	}
	/**
	 * 
	 * @param response
	 * @param url 相对于vfs根的路径
	 * @throws Exception
	 */
	public static void showVFSImage(HttpServletResponse response, String url)  throws Exception{
		if(StringUtils.isEmpty(url)) {
			return;
		}
		if(url.contains(BREAK_STR)){
			url=url.substring(0, url.indexOf(BREAK_STR));
		}
		showImage(response, VFS_ROOT_PATH + url);
	}
	
	public static void main(String[] args){
		System.out.println(listChildFilesAndDirs("3/feed").size());
	}
}
